<template>
	<view class="e-drag-list" :style="[listStyle]">
		<view class="e-drag-list__substitute" id="EDragListSubstituteBox" :style="[substituteStyle, itemStyle]"
			v-show="substituteShow">
			<view class="e-drag-list__substitute__content" :style="[substituteContentStyle, itemStyle]">
				<view class="e-drag-list__substitute__content-left">
					<slot name="substituteBox" :index="childrenIndex">
						<view>
							<text class="e-drag-list__substitute__content-left__label"
								v-if="childrenLabel">{{ childrenLabel }}</text>
						</view>
					</slot>
				</view>
				<view>
					<slot name="substituteIcon">
						<e-icon :name="props.icon" :color="props.iconColor" :size="props.iconSize"></e-icon>
					</slot>
				</view>
			</view>
		</view>
		<scroll-view class="e-drag-list__scroll" :id="scrollViewId" ref="dragListScrollRef" :data-guid="dataViewId"
			:scroll-y="true" :scroll-top="scrollViewTop" :scroll-with-animation="scrollAnimation" @scroll="drag.scroll">
			<slot></slot>
		</scroll-view>
		<!-- 数据跳板 -->
		<view id="EDragListDataView" style="display: none !important;" :data-guid="dataViewId" :prop="wxsData"
			:change:prop="drag.receiveData"></view>
		<!-- #ifdef APP-VUE || H5 -->
		<view style="display: none !important;" :prop="renderData" :change:prop="render.runCommand"></view>
		<!-- #endif -->
	</view>
</template>

<script src="./drag.wxs" module="drag" lang="wxs"></script>
<script src="./render.js" module="render" lang="renderjs"></script>
<script lang="ts">
	import {
		defineComponent
	} from 'vue'
	import mpMixin from "../../libs/mixin/mpMixin";
	/**
	 * 拖拽排序组件 HM-dragSort
	 * @description 拖拽排序组件 HM-dragSort
	 * @property {String|Number} 		Height					列表高度 
	 * @property {Boolean} 				feedbackGenerator		是否拖动触感反馈  
	 * @property {Boolean} 				autoScroll				是否拖拽至边缘自动滚动列表  
	 * @property {Number} 				longTouchTime			触发长按时长,单位:ms,默认300ms,不支持微信小程序
	 * @property {Boolean} 				disabled				是否禁用 （默认 false）
	 * @property {String|Number} 		itemHeight				子项高度，单位px 
	 * @property {String} 				icon					右侧图标
	 * @property {String} 				iconColor				右侧图标颜色
	 * @property {String} 				iconSize				右侧图标大小
	 * @property {Boolean} 				borderBottom			是否显示下划线边框
	 * @property {String} 				borderColor				下划线颜色
	 * @property {String} 				bgColor					列表背景颜色
	 * @event {Function} change 行位置发生改变时触发事件 返回值:{index:'原始下标',toIndex:'被拖动到的下标',item:'拖动行数据'}   
	 * @event {Function} confirm 拖拽结束且行位置发生了改变触发事件 返回值:{index:'原始下标',toIndex:'被拖动到的下标',item:'拖动行数据',list:'整个列表拖动后的数据'}  
	 */
	export default defineComponent({
		name: "e-drag-list",
		mixins: [mpMixin],
	})
</script>
<script lang="ts" setup>
	import {
		ref,
		reactive,
		computed,
		toRef,
		toRefs,
		nextTick,
		onMounted,
		provide,
		getCurrentInstance
	} from 'vue'
	import {
		useDefaultProps
	} from '../../libs/hooks'
	import {
		dragListProps,
		dragListEmits
	} from './props';
	import {
		dragListContextKey,
		type DragListItemContext,
		type DragListContext
	} from '../../libs/tokens'
	import {
		nanoid,
		testNumber,
		getRect,
		getNvueRect
	} from "../../libs/utils";
	import {
		addUnit
	} from "../../libs/utils/_utils";
	import componentsProps, {
		type DefaultProps
	} from "../../libs/config/componentsProps";

	const {
		dragList: dragListDefaultProps
	} = toRefs(componentsProps);

	const ctx = getCurrentInstance()

	const _props = defineProps(dragListProps)

	const props = useDefaultProps<DefaultProps['dragList']>(_props, dragListDefaultProps);
	const emit = defineEmits(dragListEmits)

	const dataViewId = nanoid()
	// 用于获取dom信息
	const dragListScrollRef = ref(null)
	const scrollViewId = `EDragList_${dataViewId}`
	const listStyle = computed(() => {
		return {
			height: addUnit(props.value.height),
			backgroundColor: props.value.bgColor
		}
	})


	let scrollAnimation: boolean
	// 是否开启滚动动画
	// #ifdef APP-PLUS|| H5
	scrollAnimation = false
	// #endif
	// #ifdef MP-WEIXIN
	scrollAnimation = true
	// #endif

	// 滚动位置
	const scrollViewTop = ref(0)
	const renderData = ref<any>(null)





	const nowElId = ref('')
	const children = reactive<Array<DragListItemContext>>([])
	const childrenItem = ref<DragListItemContext | null>(null)
	const childrenIndex = ref<number>(-1)
	const childrenLabel = computed(() => {
		return childrenItem.value?.props?.label
	})
	const itemStyle = computed(() => {
		return {
			height: addUnit(props.value.itemHeight),

		}
	})

	const substituteShow = ref(false)
	const substituteStyle = reactive({
		top: '0'
	})
	const substituteContentStyle = reactive({
		transform: '',
		'-webkit-transform': '',
		backgroundColor: props.value.bgColor
	})
	const showDragEl = (uid: string, top: number) => {
		nowElId.value = uid
		childrenItem.value = children.find(item => item.uid === uid)!
		childrenIndex.value = Number(childrenItem.value.index)
		substituteStyle.top = addUnit(top)
		substituteShow.value = true
	}
	const handleMove = (y: number) => {
		const transform = `translate3d(0,${y}px,10px)`
		substituteContentStyle.transform = transform
		substituteContentStyle['-webkit-transform'] = transform
	}
	const hiddDragEl = () => {
		nowElId.value = ''
		substituteStyle.top = '0'
		substituteShow.value = false
	}

	// 是否正在拖拽元素
	let isHoldTouch = false
	// 是否正在执行滚动
	let isScrolling = false
	// 滚动视图的定时器
	let scrollTimer = 0
	// 容器高度
	let scrollHeight = ref(testNumber(props.value.height) ? Number(props.value.height) : 0)

	// 控制自动滚动视图
	const pageScroll: DragListContext['pageScroll'] = (e) => {
		// 滚动 up-上滚动 down-下滚动
		if (e.command == "up" || e.command == "down") {
			if (!isHoldTouch) {
				isHoldTouch = true;
				scrollViewTop.value = e.scrollTop;
			}
			if (isScrolling) {
				return;
			};
			isScrolling = true;

			if (wxsConfig.isMp) {
				// 微信小程序执行视图滚动
				scrollTimer && clearInterval(scrollTimer);
				let maxHeight = Number(props.value.itemHeight) * context.length + 1 - scrollHeight.value;
				let runTick = true;
				// 逻辑层传递到视图层需要时间，可能会出现滚动不流畅现象
				scrollTimer = setInterval(() => {
					if (!runTick) {
						return;
					}
					runScroll(e.command, maxHeight);
					runTick = false;
					nextTick(() => runTick = true)
				}, 16.6)
			}
			let data = {
				length: context.length,
				itemHeight: props.value.itemHeight,
				scrollHeight: scrollHeight.value,
				...e
			}

			// APP端和H5端 执行renderjs的滚动
			renderData.value = data
			return;


		}
		// 停止滚动
		if (e.command == "stop") {
			// #ifdef APP-PLUS || H5
			// 停止指定传递到renderjs
			renderData.value = e
			// #endif
			isScrolling && stopScroll();
		}
	}
	// 微信端的滚动
	const runScroll = (command: string, maxHeight: number) => {
		if (command == "up") {
			scrollViewTop.value -= 5
		}
		if (command == "down") {
			scrollViewTop.value += 5;
		}
		if (scrollViewTop.value < 0) {
			scrollViewTop.value = 0;
			clearInterval(scrollTimer);
		}
		if (scrollViewTop.value > maxHeight) {
			scrollViewTop.value = maxHeight;
			clearInterval(scrollTimer);
		}
	}
	//停止滚动
	const stopScroll = () => {
		scrollTimer && clearInterval(scrollTimer);
		isScrolling = false;
		scrollViewTop.value = 0
	}

	const updateChildSort: DragListContext['updateChildSort'] = (uid, sort) => {
		const child = children.find(item => item.uid === uid)
		child?.updateMoveSort?.(sort)
	}
	const updateRowsSort: DragListContext['updateRowsSort'] = (sortList) => {
		let arr: Array<{ index: string | number, sort: number }> = []
		children.forEach((item, index) => {
			let sort = sortList[index]
			item.updateSort(sort)
			arr.push({
				index: item.index,
				sort
			})
		})
		emit('change', arr)
	}

	// 暴露给子组件调用的方法，每个子组件都会在初始化时调用
	const addField: DragListContext['addField'] = item => {
		children.push(item)
		context.length++
	}
	// 删除某个子组件
	const removeField: DragListContext['removeField'] = (uid) => {
		const index = children.findIndex((item) => item.uid === uid)
		if (index !== -1) {
			context.length--
			children.splice(index, 1)
		}
	}

	const context = reactive({
		props,
		id: dataViewId,
		length: 0,
		isMove: substituteShow,
		addField,
		removeField,
		showDragEl,
		handleMove,
		hiddDragEl,
		pageScroll,
		updateChildSort,
		updateRowsSort
	})
	let isMp: boolean | undefined
	// #ifdef APP-PLUS|| H5
	isMp = false
	// #endif
	// #ifdef MP-WEIXIN
	isMp = true
		// #endif
	const wxsConfig = reactive({
		id: dataViewId,
		children,
		longTouchTime: props.value.longTouchTime,
		itemHeight: props.value.itemHeight,
		length: toRef(context, 'length'),
		autoScroll: props.value.autoScroll,
		scrollHeight,
		isMp
		
	})

	// wxs数据
	const wxsData = computed(() => JSON.stringify({
		...wxsConfig,
		updateTime: new Date().getTime()
	}))

	const init = () => {
		// #ifndef APP-NVUE
		getRect(ctx, `#${scrollViewId}`).then(size => {
			scrollHeight.value = size.height!
		})
		// #endif

		// #ifdef APP-NVUE
		getNvueRect(dragListScrollRef.value).then(size => {
			scrollHeight.value = size.height!
		})
		// #endif



	}

	onMounted(init)

	provide(
		dragListContextKey,
		context
	)
</script>

<style lang="scss" scoped>
	@import "../../libs/theme/drag-list.scss";
</style>
